﻿// Silver.Globe, version 0.11 for Silverlight 1.1 Alpha
// Copyright © Florian Krüsch (xaml-kru.com)
// xaml-kru.com/silverglobe
// This source is subject to the Microsoft Public License (Ms-PL).
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx.
// All other rights reserved.

using System;
using System.Windows.Controls;
using System.IO;
using System.Windows.Browser;
using SilverGlobe.Data;
using SilverGlobe.Math3D;
using System.Windows.Input;
using System.Windows;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using System.Windows.Threading;

namespace SilverGlobe
{
    public partial class SilverGlobeControl : Canvas
    {
        #region Members

        private DispatcherTimer _timer;
        private RotationController _rotationController;
        private FrameworkElement _root;

        #endregion

        #region Events

        /// <summary>
        /// Event raised when the mouse is over a selectable location.
        /// </summary>
        public event EventHandler<LocationEventArgs> LocationMouseOver;

        /// <summary>
        /// Event raised when the mouse moves out of a selectable location.
        /// </summary>
        public event EventHandler<LocationEventArgs> LocationMouseOut;

        /// <summary>
        /// Event raised when a location is selected with a mouse click.
        /// </summary>
        public event EventHandler<LocationEventArgs> LocationSelected;

        /// <summary>
        /// Event raised when the user starts rotating the globe by dragging with the mouse.
        /// </summary>
        public event EventHandler DragRotate;

        #endregion

        #region Constructor

        public SilverGlobeControl()
        {
            Loaded += _Loaded;

            InitializeComponent();
            InitShapes();            
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initialize the control with a list of selectable locations.
        /// </summary>
        public void SetLocations(IEnumerable<Location> locations)
        {
            _locationSelector.SetLocations(locations);
        }

        #endregion

        #region Event handlers

        private void _Loaded(object sender, EventArgs e)
        {
            _root = (FrameworkElement)Application.Current.RootVisual;
            SubscribeEvents();

            _timer = new DispatcherTimer();
            _timer.Tick += Timer_Tick;
            _timer.Interval = TimeSpan.FromMilliseconds(10);
            _timer.Start();

            _rotationController = new RotationController(_globe, _timer);     
        }

        private void Timer_Tick(Object sender, EventArgs e)
        {
            _globe.Update();
            _locationSelector.Update(_globe);
            _rotationController.Update();
        }

        private void Location_MouseOut(object sender, LocationEventArgs e)
        {
            EventHandler<LocationEventArgs> h = LocationMouseOut;
            if (h != null) h(this, e);
        }

        private void Location_MouseOver(object sender, LocationEventArgs e)
        {
            if (_rotationController.DoesLocationAnimation) return;

            UnsubscribeMouseRelease();
            _rotationController.Stop();

            EventHandler<LocationEventArgs> h = LocationMouseOver;
            if (h != null) h(this, e);
        }

        private void Location_Selected(object sender, LocationEventArgs e)
        {
            if (_rotationController.DoesLocationAnimation) return;

            GeoPosition pos = e.Location.Position;
            _rotationController.AnimateTo(pos);

            EventHandler<LocationEventArgs> h = LocationSelected;
            if (h != null) h(this, e);
        }

        private void Globe_MouseDown(object sender, MouseEventArgs e)
        {
            SubscribeMouseRelease();

            _rotationController.BindToMousePosition();

            EventHandler h = DragRotate;
            if (h != null) h(this, EventArgs.Empty);
        }

        private void _MouseRelease(object sender, EventArgs e)
        {
            UnsubscribeMouseRelease();

            _rotationController.EaseOut();
        }

        #endregion

        #region Implementation

        private void InitShapes()
        {
            _globe.InitShapes(ContinentShapes.Africa,
                              ContinentShapes.America,
                              ContinentShapes.Australia,
                              ContinentShapes.Eurasia,
                              ContinentShapes.Antarctica
                              );
        }

        private void SubscribeEvents()
        {
            _locationSelector.LocationSelected += Location_Selected;
            _locationSelector.LocationMouseOver += Location_MouseOver;
            _locationSelector.LocationMouseOut += Location_MouseOut;

            _globe.MouseLeftButtonDown += Globe_MouseDown;
        }

        private void UnsubscribeMouseRelease()
        {
            _root.MouseLeftButtonUp -= _MouseRelease;
            _root.MouseLeave -= _MouseRelease;
        }

        private void SubscribeMouseRelease()
        {
            UnsubscribeMouseRelease();

            _root.MouseLeftButtonUp += _MouseRelease;
            _root.MouseLeave += _MouseRelease;
        }

        #endregion  
    }
}

